iT邦幫忙

2024 iThome 鐵人賽

DAY 26
0

第 25 天:Python 的單元測試與自動化測試

課程目標:

今天的主題是 Python 中的單元測試(Unit Testing)及自動化測試。測試是一個確保程式碼質量的重要步驟,特別是在開發過程中進行迭代更新時。這一課程將教你如何撰寫測試來確保程式碼的正確性,並學習 Python 中的 unittest 模組來進行單元測試。


1. 什麼是單元測試?

單元測試是針對應用程式中的最小可測單元——通常是一個函數或方法——進行測試。它的目的是驗證這些最小單位是否按預期工作。

為什麼單元測試很重要?

  • 確保程式碼邏輯正確
  • 在進行修改後,確保沒有破壞現有功能
  • 減少手動測試的需求,提升效率

2. 使用 unittest 模組進行單元測試

Python 標準庫內建了 unittest 模組,讓我們能夠輕鬆撰寫單元測試。

撰寫一個簡單的測試

首先,我們將撰寫一個簡單的函數,並使用 unittest 進行測試。

# 被測試的函數
def add(a, b):
    return a + b

# 單元測試的範例
import unittest

class TestAddFunction(unittest.TestCase):

    def test_add(self):
        self.assertEqual(add(2, 3), 5)  # 測試是否 2 + 3 等於 5
        self.assertEqual(add(-1, 1), 0)  # 測試是否 -1 + 1 等於 0
        self.assertEqual(add(0, 0), 0)   # 測試是否 0 + 0 等於 0

if __name__ == '__main__':
    unittest.main()

在這裡,我們使用 unittest.TestCase 創建了一個測試類,並且在裡面定義了 test_add 方法來測試 add 函數。self.assertEqual 用來檢查實際結果是否等於預期結果。


3. 測試的其他斷言方法

unittest 提供了多種斷言方法來幫助我們驗證測試結果:

  • assertEqual(a, b): 測試 a 是否等於 b
  • assertTrue(x): 測試 x 是否為真
  • assertFalse(x): 測試 x 是否為假
  • assertIsNone(x): 測試 x 是否為 None
  • assertIn(a, b): 測試 a 是否在 b 中
  • assertRaises(exc, fun, *args): 測試某個函數是否拋出指定的異常
例子:測試是否有例外情況
def divide(a, b):
    if b == 0:
        raise ValueError("不能除以 0")
    return a / b

class TestDivideFunction(unittest.TestCase):

    def test_divide(self):
        self.assertEqual(divide(10, 2), 5)
        self.assertRaises(ValueError, divide, 10, 0)

4. 測試套件(Test Suite)

當你的程式有很多測試時,可以使用測試套件將多個測試組合起來,以便同時執行。這樣可以提高測試的組織性與效率。

def multiply(a, b):
    return a * b

class TestMultiplyFunction(unittest.TestCase):
    
    def test_multiply(self):
        self.assertEqual(multiply(3, 5), 15)

# 創建測試套件
def suite():
    suite = unittest.TestSuite()
    suite.addTest(TestAddFunction('test_add'))
    suite.addTest(TestMultiplyFunction('test_multiply'))
    return suite

if __name__ == '__main__':
    runner = unittest.TextTestRunner()
    runner.run(suite())

5. 自動化測試

我們可以結合 unittest 和持續集成工具(如 Jenkins、GitHub Actions)來自動化執行測試,當有新程式碼合併時,自動進行測試,確保沒有引入新的錯誤。

例子:整合 GitHub Actions 進行自動化測試
  1. 創建 .github/workflows/python-app.yml
  2. 編寫以下內容:
name: Python application

on:
  push:
    branches:
      - main

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.x'
    - name: Install dependencies
      run: pip install -r requirements.txt
    - name: Run tests
      run: python -m unittest discover

當你將程式碼推送到 GitHub 時,測試將自動運行。


6. 實作練習

練習 1: 撰寫一個函數,返回兩個數字的最大值,並為此函數撰寫單元測試。

練習 2: 創建一個簡單的字典查找程式,當查找不到指定的鍵時應拋出 KeyError,並撰寫測試以檢查此行為。

這些練習涉及編寫簡單的函數並為其撰寫單元測試。單元測試用於驗證程式的行為是否符合預期,確保程式在未來的修改中能夠穩定運行。


練習 1:撰寫一個函數,返回兩個數字的最大值,並為此函數撰寫單元測試
步驟:
  1. 寫一個簡單的函數來返回兩個數字中的最大值。
  2. 使用 Python 的 unittest 模組來撰寫單元測試,確保該函數在各種情況下返回正確結果。
範例代碼:
  1. 函數定義:
def max_value(a, b):
    return max(a, b)
  1. 單元測試代碼:
import unittest

class TestMaxValue(unittest.TestCase):
    
    def test_positive_numbers(self):
        self.assertEqual(max_value(3, 5), 5)
        
    def test_negative_numbers(self):
        self.assertEqual(max_value(-3, -5), -3)
        
    def test_equal_numbers(self):
        self.assertEqual(max_value(4, 4), 4)
        
    def test_mixed_sign_numbers(self):
        self.assertEqual(max_value(-10, 5), 5)

# 運行單元測試
if __name__ == '__main__':
    unittest.main()
解釋:
  • max_value 函數簡單地使用內建的 max() 函數來返回兩個數中的最大值。
  • 單元測試使用了 unittest 模組,測試了不同情況下函數的輸出是否正確。
  • 測試範例包括正數、負數、相等的數字以及混合正負數字的情況。

練習 2:創建一個簡單的字典查找程式,當查找不到指定的鍵時應拋出 KeyError,並撰寫測試以檢查此行為
步驟:
  1. 創建一個查找字典中值的函數,當找不到鍵時拋出 KeyError
  2. 使用 unittest 模組來撰寫單元測試,檢查該函數是否正確拋出 KeyError
範例代碼:
  1. 函數定義:
def lookup(dictionary, key):
    if key not in dictionary:
        raise KeyError(f"Key '{key}' not found in dictionary.")
    return dictionary[key]
  1. 單元測試代碼:
import unittest

class TestLookupFunction(unittest.TestCase):
    
    def setUp(self):
        self.data = {"apple": 1, "banana": 2, "orange": 3}
    
    def test_existing_key(self):
        self.assertEqual(lookup(self.data, "apple"), 1)
    
    def test_key_error(self):
        with self.assertRaises(KeyError):
            lookup(self.data, "grape")

# 運行單元測試
if __name__ == '__main__':
    unittest.main()
解釋:
  • lookup 函數檢查字典中是否存在指定的鍵,若不存在則拋出 KeyError
  • 單元測試使用了 setUp 方法來設置測試前的初始條件,並測試了兩種情況:查找存在的鍵和查找不存在的鍵。
  • assertRaises 用於確認在查找不到鍵時正確地拋出了 KeyError

這兩個練習展示了如何編寫基本函數並使用 unittest 進行單元測試,以驗證程式的正確性和穩定性。在實際開發中,這樣的測試能夠防止程式中的潛在錯誤並幫助程式進行維護和升級。


7. 小結

今天我們學習了如何在 Python 中撰寫單元測試並自動化測試流程。通過測試,我們能夠確保程式碼的穩定性和質量,並且在進行程式碼更新時可以自動檢測是否有新的錯誤出現。


上一篇
跟著 ChatGPT成為程式大佬!Python 正規表示式
下一篇
跟著 ChatGPT成為程式大佬!Python 測試驅動開發(Test-Driven Development, TDD)
系列文
如果讓chatgpt參加iThome鐵人賽,他竟然寫出...!?31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言